/*
Copyright (C) 2011 Ing. Marek Hubal

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "core.h"
#include "IBitStream.h"

#define ALLOCATE_BUFFER_SIZE 1024*1024

// .....................................................................................................................
// Konstruktor
// .....................................................................................................................
IBitStream::IBitStream(void) {
	Data = NULL; // Inicializacia
	Capacity = 0;
	FCurrentIndex = 0; FCurrentMask = 0x80;
}
// .....................................................................................................................
// .....................................................................................................................
// Destruktor
// .....................................................................................................................
IBitStream::~IBitStream(void) {
	Clear(); // Uvolnim pamat
}
// .....................................................................................................................
// .....................................................................................................................
// Uvolnenie objektu z pamate
// .....................................................................................................................
void __fastcall IBitStream::Clear(void) {
	if (Data != NULL) delete Data; // Zrusim data
	Data = NULL; // Inicializacia
	Capacity = 0;
	FCurrentIndex = 0; FCurrentMask = 0x80;
}
// .....................................................................................................................
// .....................................................................................................................
// Realokacia pamate
// .....................................................................................................................
void __fastcall IBitStream::Reallocate(void) {
	Capacity += ALLOCATE_BUFFER_SIZE; // Urcim novu kapacitu
	PBYTE NW = new BYTE[Capacity]; // Alokujem novu pamat
	if (Data != NULL) { // Uz je alokovana pamat?
		memcpy(NW, Data, Capacity - ALLOCATE_BUFFER_SIZE); // Skopirujem pamat
		delete Data; // Zrusim staru pamat
	}
	Data = NW; // Ulozim ako novu pamat
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie bitu
// .....................................................................................................................
void __fastcall IBitStream::AddBIT(bool pValue) {
	_AddBIT(pValue); // Volam protected metodu
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie bitu
// .....................................................................................................................
void __fastcall IBitStream::_AddBIT(bool pValue) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) Reallocate(); // Treba alokovat?
		Data[FCurrentIndex] = 0; // Inicializujem
	}
	if (pValue) Data[FCurrentIndex] |= FCurrentMask; // Nastavim bit ak je vstup 1

	if (FCurrentMask == 0x1) { // Posledny bit v bajte?
		FCurrentMask = 0x80; // Zaciatok
		FCurrentIndex++; 
	}
	else FCurrentMask >>= 1; // Zrolujem masku
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT8
// .....................................................................................................................
void __fastcall IBitStream::AddBYTE(BYTE pValue) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) Reallocate(); // Treba alokovat?
		Data[FCurrentIndex++] = pValue; // Inicializujem
	}
	else {
		BYTE M = 0x80;
		for (INT32 i = 0; i < 8; i++) { // Zaradim jednotlive bity zadanej hodnoty
			_AddBIT((M & pValue) != 0);
			M >>= 1;
		}
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Pretocenie streamu na zaciatok
// .....................................................................................................................
void __fastcall IBitStream::Rewind(void) {
	FCurrentIndex = 0; FCurrentMask = 0x80;
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT16
// .....................................................................................................................
void __fastcall IBitStream::AddWORD(UINT16 pValue) {
	AddBYTE((pValue >> 8) & 0xff);
	AddBYTE(pValue & 0xff);
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT32
// .....................................................................................................................
void __fastcall IBitStream::AddDWORD(UINT32 pValue) {
	AddWORD((pValue >> 16) & 0xffff);
	AddWORD(pValue & 0xffff);
}
// .....................................................................................................................
// .....................................................................................................................
// Zaradenie UINT64
// .....................................................................................................................
void __fastcall IBitStream::AddQWORD(UINT64 pValue) {
	AddDWORD((pValue >> 32) & 0xffffffff);
	AddDWORD(pValue & 0xffffffff);
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie bitu
// .....................................................................................................................
bool __fastcall IBitStream::_GetBIT(void) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) return false; // Som mimo?
	}
	bool B = (Data[FCurrentIndex] & FCurrentMask) != 0;
	if (FCurrentMask == 0x1) { // Posledny bit v bajte?
		FCurrentMask = 0x80; // Zaciatok
		FCurrentIndex++; 
	}
	else FCurrentMask >>= 1; // Zrolujem masku
	return B;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie bitu
// .....................................................................................................................
bool __fastcall IBitStream::GetBIT(void) {
	return _GetBIT(); // Volam protected metodu
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT8
// .....................................................................................................................
BYTE __fastcall IBitStream::GetBYTE(void) {
	if (FCurrentMask == 0x80) {
		if (FCurrentIndex >= Capacity) return 0; // Som uz mimo?
		return Data[FCurrentIndex++]; // Vratim naraz 8 bitov
	}
	else {
		BYTE M = 0x80; BYTE Res = 0; 
		for (INT32 i = 0; i < 8; i++) { // Nacitam 8 bitov
			if (_GetBIT()) Res |= M;
			M >>= 1;
		}
		return Res; // Vratim poskladany vysledok
	}
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT16
// .....................................................................................................................
UINT16 __fastcall IBitStream::GetWORD(void) {
	UINT16 Res = ((UINT16)GetBYTE()) << 8;
	Res |= (UINT16)GetBYTE();
	return Res;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT32
// .....................................................................................................................
UINT32 __fastcall IBitStream::GetDWORD(void) {
	UINT32 Res = ((UINT32)GetWORD()) << 16;
	Res |= (UINT32)GetWORD();
	return Res;
}
// .....................................................................................................................
// .....................................................................................................................
// Nacitanie UINT64
// .....................................................................................................................
UINT64 __fastcall IBitStream::GetQWORD(void) {
	UINT64 Res = ((UINT64)GetDWORD()) << 32;
	Res |= (UINT64)GetDWORD();
	return Res;
}
// .....................................................................................................................
